package drds.plus.util;

import drds.plus.common.model.ThreadLocalString;
import drds.plus.common.model.comparative.And;
import drds.plus.common.model.comparative.Comparative;
import drds.plus.common.model.comparative.Or;
import drds.plus.common.model.hint.DirectlyRouteCondition;
import drds.plus.common.model.hint.RouteType;
import drds.plus.common.model.hint.RuleRouteCondition;
import drds.plus.common.thread_local.ThreadLocalMap;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * 方便业务调用直接接口的方法<br/>
 * 别太纠结这混乱的包定义,一切都是为了以前老的客户端package定义
 */
public class RouteHelper {


    /**
     * 直接在某个库上,执行一条sql 这时候TDDL只做两件事情，第一个是协助判断是否在事务状态。 第二个事情是，进行表名的替换。
     *
     * @param dataNodeId       dbIndex列表
     * @param virtualTableName 逻辑表名
     * @param routeType        决定这个hint是在连接关闭的时候清空，还是在执行时就清空
     */
    public static void executeByDBAndTab(String dataNodeId, String virtualTableName, RouteType routeType, String... tableNames) {
        DirectlyRouteCondition directlyRouteCondition = new DirectlyRouteCondition();
        if (tableNames == null) {
            throw new IllegalArgumentException("realTableNamesStringSet is null");
        }

        for (String tableName : tableNames) {
            directlyRouteCondition.addRealTableNamesString(tableName);
        }
        directlyRouteCondition.setVirtualTableNamesString(virtualTableName);
        directlyRouteCondition.setDataNodeId(dataNodeId);
        directlyRouteCondition.setRouteType(routeType);
        ThreadLocalMap.put(ThreadLocalString.DB_SELECTOR, directlyRouteCondition);
    }

    /**
     * 直接在某个库上,执行一条sql 这时候TDDL只做两件事情，第一个是协助判断是否在事务状态。 第二个事情是，进行表名的替换。
     *
     * @param dbindex    dbIndex列表
     * @param logicTable 逻辑表名
     * @param table      实际表名
     */
    public static void executeByDBAndTab(String dbindex, String logicTable, String... table) {
        executeByDBAndTab(dbindex, logicTable, RouteType.flush_on_execute, table);
    }

    /**
     * 直接在某个库上,执行一条sql，并允许进行多组表名的替换，主要目标是join的sql tddl should do : 1. 判断事务是否可执行。 2.
     * 将多组表名进行替换。
     *
     * @param dbIndex  dbIndex列表
     * @param tableMap 源表名->目标表名的map
     */
    public static void executeByDBAndTab(String dbIndex, Map<String, String> tableMap) {
        executeByDBAndTab(dbIndex, tableMap, RouteType.flush_on_execute);
    }

    /**
     * 直接在某个库上,执行一条sql，并允许进行多组表名的替换，主要目标是join的sql
     *
     * @param dbIndex   dbIndex列表
     * @param tableMap  源表名->目标表名的map
     * @param routeType 决定这个hint是在连接关闭的时候清空，还是在执行时就清空
     */
    public static void executeByDBAndTab(String dbIndex, Map<String, String> tableMap, RouteType routeType) {
        executeByDBAndTab(dbIndex, Arrays.asList(tableMap), routeType);
    }

    /**
     * 直接在某个库上,执行一条sql，并允许进行多组表名的替换，主要目标是join的sql
     *
     * @param dataNodeId                             dbIndex列表
     * @param virtualTableNameToRealTableNameMapList 源表名->目标表名的map
     * @param routeType                              决定这个hint是在连接关闭的时候清空，还是在执行时就清空
     */
    public static void executeByDBAndTab(String dataNodeId, List<Map<String, String>> virtualTableNameToRealTableNameMapList) {
        executeByDBAndTab(dataNodeId, virtualTableNameToRealTableNameMapList, RouteType.flush_on_execute);
    }

    /**
     * 直接在某个库上,执行一条sql，并允许进行多组表名的替换，主要目标是join的sql
     *
     * @param dataNodeId dbIndex列表
     * @param tableMap   源表名->目标表名的map
     * @param routeType  决定这个hint是在连接关闭的时候清空，还是在执行时就清空
     */
    public static void executeByDBAndTab(String dataNodeId, List<Map<String, String>> virtualTableNameToRealTableNameMapList, RouteType routeType) {
        DirectlyRouteCondition directlyRouteCondition = new DirectlyRouteCondition();
        if (virtualTableNameToRealTableNameMapList == null) {
            throw new IllegalArgumentException("tableMap is null");
        }

        String virtualTableName = null;
        for (Map<String, String> virtualTableNameToRealTableNameMap : virtualTableNameToRealTableNameMapList) {
            StringBuilder virtualTableName2 = new StringBuilder();
            StringBuilder tableName = new StringBuilder();
            int size = virtualTableNameToRealTableNameMap.size();
            int i = 1;
            for (Map.Entry<String, String> entry : virtualTableNameToRealTableNameMap.entrySet()) {
                virtualTableName2.append(entry.getKey());
                tableName.append(entry.getValue());
                if (i < size) {
                    virtualTableName2.append(',');
                    tableName.append(',');
                }
            }

            if (virtualTableName == null) {
                virtualTableName = virtualTableName2.toString();
            } else if (!virtualTableName.equalsIgnoreCase(virtualTableName2.toString())) {
                throw new IllegalArgumentException("多表替换时逻辑表出现不一致");
            }
            directlyRouteCondition.addRealTableNamesString(tableName.toString());
        }

        directlyRouteCondition.setVirtualTableNamesString(virtualTableName);
        directlyRouteCondition.setDataNodeId(dataNodeId);
        directlyRouteCondition.setRouteType(routeType);
        ThreadLocalMap.put(ThreadLocalString.DB_SELECTOR, directlyRouteCondition);
    }

    /**
     * 根据db index 执行一条sql. sql就是你通过Ibatis输入的sql. 只做一件事情，就是协助判断是否需要进行事务
     *
     * @param dataNodeId dbIndex列表
     * @param routeType  决定这个hint是在连接关闭的时候清空，还是在执行时就清空
     */
    public static void executeByDB(String dataNodeId, RouteType routeType) {
        DirectlyRouteCondition condition = new DirectlyRouteCondition();
        condition.setDataNodeId(dataNodeId);
        condition.setRouteType(routeType);
        ThreadLocalMap.put(ThreadLocalString.DB_SELECTOR, condition);
    }

    /**
     * 根据db index 执行一条sql. sql就是你通过Ibatis输入的sql. 只做一件事情，就是协助判断是否需要进行事务
     *
     * @param dataNodeId dbIndex列表
     */
    public static void executeByDB(String dataNodeId) {
        executeByDB(dataNodeId, RouteType.flush_on_execute);
    }


    /**
     * 根据条件选择数据库进行执行sql
     */
    public static void executeByCondition(String virtualTableName, String key, Comparable<?> comparable) {
        executeByCondition(virtualTableName, key, comparable, RouteType.flush_on_execute);
    }

    /**
     * 根据条件选择数据库进行执行sql
     */
    public static void executeByCondition(String virtualTableName, String key, Comparable<?> comparable, RouteType routeType) {
        RuleRouteCondition simpleCondition = new RuleRouteCondition();
        simpleCondition.setVirtualTableNamesString(virtualTableName);
        simpleCondition.put(key, comparable);
        simpleCondition.setRouteType(routeType);
        ThreadLocalMap.put(ThreadLocalString.ROUTE_CONDITION, simpleCondition);
    }

    /**
     * 指定多列的条件
     */
    public static void executeByAdvancedCondition(String virtualTableName, Map<String, Comparable<?>> columnNameToComparativeMap, RouteType routeType) {
        RuleRouteCondition ruleRouteCondition = new RuleRouteCondition();
        ruleRouteCondition.setVirtualTableNamesString(virtualTableName);
        for (Map.Entry<String, Comparable<?>> entry : columnNameToComparativeMap.entrySet()) {
            ruleRouteCondition.put(entry.getKey(), entry.getValue());
        }
        ruleRouteCondition.setRouteType(routeType);
        ThreadLocalMap.put(ThreadLocalString.ROUTE_CONDITION, ruleRouteCondition);
    }

    /**
     * 指定多列的条件
     */
    public static void executeByAdvancedCondition(String logicTable, Map<String, Comparable<?>> columnNameToComparativeMap) {
        executeByAdvancedCondition(logicTable, columnNameToComparativeMap, RouteType.flush_on_execute);
    }

    /**
     * 构建or条件
     */
    public static Comparative or(Comparative parent, Comparative target) {
        if (parent == null) {
            Or or = new Or();
            or.addComparative(target);
            return or;
        } else {
            if (parent instanceof Or) {
                ((Or) parent).addComparative(target);
                return parent;
            } else {
                Or or = new Or();
                or.addComparative(parent);
                or.addComparative(target);
                return or;
            }
        }
    }

    /**
     * 构建and条件
     */
    public static Comparative and(Comparative parent, Comparative target) {
        if (parent == null) {
            And and = new And();
            and.addComparative(target);
            return and;
        } else {
            if (parent instanceof And) {

                And and = ((And) parent);
                if (and.getComparativeList().size() == 1) {
                    and.addComparative(target);
                    return and;
                } else {
                    And andNew = new And();
                    andNew.addComparative(and);
                    andNew.addComparative(target);
                    return andNew;
                }

            } else {
                And and = new And();
                and.addComparative(parent);
                and.addComparative(target);
                return and;
            }
        }
    }
}
